//
// Created by QU on 24-6-26.
//

//新增宏VA_OPT

/*
从C99标准开始，C语言引入了可变参数宏__VA_ARGS__，而顺理成章的C++11标准也将其纳入标准当中。__VA_ARGS__常见的用法
集中于打印日志上，例如：
*/

//#define LOG(msg, …) printf("[" __FILE__ ":%d] " msg, __LINE__,__VA_ARGS__)
//LOG("Hello %d", 2020);

/*
LOG的使用和printf非常相似，并且可以很方便地将代码文件和行数记录到日志当中。不过它们也并非完全相同，
因为对于函数printf来说，除了第一个参数以外的其他参数都是可选的：
*/

//printf("Hello 2020"); // 编译成功
//而对于LOG宏来说，这种写法是非法的：
//LOG("Hello 2020");
//上面这句代码展开后应该是：
//printf("[" __FILE__ ":%d] " "Hello 2020", __LINE__, );

/*
很明显，函数的最后多出了一个逗号，在GCC和CLang上编译都会报错。虽然在Visual Studio 2019上不会报错，但这并不具备通用
    性。实际上GCC和CLang也有类似扩展，只不过没有隐式地提供，我们可以使用##连接逗号和__VA_ARGS__：
*/

/*
#define LOG(msg, …) printf("[" __FILE__ ":%d] " msg, __LINE__,
##__VA_ARGS__)
LOG("Hello 2020"); // GCC和CLang编译成功
*/

/*
为了用更加标准的方法解决以上问题，C++20标准引入了一个新的宏__VA_OPT__令可变参数宏更易于在可变参数为空的情况下使用。
还是以刚刚的LOG为例，我们将代码修改为：
*/

#include <cstdio>

// 前面的{  "[" __FILE__:%d "]" msg  } 作为第一个参数(第一个参数有两个字符串 "XXX" "xxx"), 如果传入的是 "aquawius",
// 替换成{  "[" __FILE__:%d "]" "aquawius" , __LINE__  }
#define LOG(msg, ...) printf("[" __FILE__ ":%d]" msg "\n", __LINE__ __VA_OPT__(,) __VA_ARGS__ )

/*
观察上面的代码可以发现，__LINE__后面的逗号被修改为__VA_OPT__(,)，这是告诉编译器这个逗号是可选的。当可变参数
不为空的时候逗号才会被替换出来，否则就会忽略这个逗号。对于下面两句日志而言：
*/

int main(int argc, const char *argv[]) {
    LOG("Hello 2020");

    LOG("Hello %d", 2020);

//由于第一句代码中没有可变参数，所以被忽略，替换结果如下：
//    printf("[" __FILE__ ":%d] " "Hello 2020", __LINE__);

//第二句代码中存在可变参数，所以被替换出来，最后结果如下：
//    printf("[" __FILE__ ":%d] " "Hello %d", __LINE__, 2020);
}